import React, { useState } from "react";
import { Upload, Button, message as antdMessage } from "antd";
import SparkMD5 from "spark-md5";
import { UploadRequestOption } from "rc-upload/lib/interface";
import axios from "axios";

interface ChunkType {
  chunk: Blob;
  name: string;
}

const Index = () => {
  const [percent, setPercent] = useState(0);

  const customRequest = async (options: UploadRequestOption) => {
    setPercent(0);
    // 1. 获取上传的文件
    const file = options.file as File;
    // 2. 根据内容生成hash   使用插件 spark-md5
    const hash = await createFileHash(file);
    // 3. 请求 检查文件接口
    const checkResult = await axios.post("/api/checkFile", { hash });
    // 4. 根据结果
    const { code, message, data } = checkResult.data;
    // 5-1 处理未上传 || 已经上传过一部分
    if (code === 200 || code === 202) {
      // 6. 将大文件进行切片
      let chunks = createFileSlice(file, hash);

      // 7. 筛选下已经上传完毕的切片
      if (data) {
        chunks = chunks.filter((v) => !data.includes(v.name));
      }
      const chunkProgress = 100 / chunks.length;
      // 8. 上传所有分片到后端
      const requestList: Promise<any>[] = [];
      // 处理高并发
      while (chunks.length > 0) {
        const item = chunks.shift() as ChunkType;
        const formData = new FormData();
        formData.append("chunk", item.chunk, item.name);
        const request = axios.post("/api/upload", formData);
        request.then(() => {
          const index = requestList.findIndex((v) => v === request);
          requestList.splice(index, 1);
          setPercent((state) => {
            options.onProgress &&
              options.onProgress({ percent: state + chunkProgress });
            return state + chunkProgress;
          });
        });
        requestList.push(request);
        // 限制请求一次最多发送6个 提高性能
        if (requestList.length >= 6) {
          await Promise.race(requestList);
        }
      }
      // 9. 等待所有切片上传成功
      await Promise.all(requestList);
      // 10. 合并分片
      const extName = file.name.slice(file.name.lastIndexOf("."));
      await axios.post("/api/mergeFile", { hash, extName });
      options.onSuccess && options.onSuccess(null);
    }

    // 5-2 已经上传过
    if (code === 201) {
      antdMessage.success(message);
      options.onProgress && options.onProgress({ percent: 100 });
      options.onSuccess && options.onSuccess(null);
    }
  };

  // 哈希函数
  const createFileHash = (file: File): Promise<string> => {
    return new Promise((resolve) => {
      var blobSlice = File.prototype.slice,
        chunkSize = 2097152, // Read in chunks of 2MB
        chunks = Math.ceil(file.size / chunkSize),
        currentChunk = 0,
        spark = new SparkMD5.ArrayBuffer(),
        fileReader = new FileReader();

      fileReader.onload = function (e) {
        if (e.target && e.target.result)
          spark.append(e.target.result as ArrayBuffer); // Append array buffer

        currentChunk++;

        if (currentChunk < chunks) {
          loadNext();
        } else {
          resolve(spark.end());
        }
      };

      fileReader.onerror = function () {
        console.warn("oops, something went wrong.");
      };

      function loadNext() {
        var start = currentChunk * chunkSize,
          end = start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
      }

      loadNext();
    });
  };

  // 切片函数
  const createFileSlice = (file: File, hash: string) => {
    const chunkSize = 2 * 1024 ** 2;
    const max = Math.ceil(file.size / chunkSize);
    const chunks: ChunkType[] = [];
    // 获取.在名字字符串所在下标
    const index = file.name.lastIndexOf(".");
    const name = file.name.slice(0, index);
    const extName = file.name.slice(index);
    for (let i = 0; i < max; i++) {
      const start = i * chunkSize;
      const chunk = file.slice(start, start + chunkSize);
      chunks.push({
        chunk: chunk,
        name: hash + "-" + i + extName,
      });
    }
    return chunks;
  };

  return (
    <div>
      <Upload
        customRequest={(options: UploadRequestOption) => customRequest(options)}
      >
        <Button type="primary">上传文件</Button>
      </Upload>
    </div>
  );
};

export default Index;
